package com.sui.bigdata.sml.web.configuration;

import com.alibaba.fastjson.JSON;
import com.baidu.disconf.client.addons.properties.ReloadablePropertiesFactoryBean;
import com.feidee.fdcommon.configuration.ConfigUpdaterKey;
import com.sui.bigdata.sml.web.dto.ModelGroupDTO;
import com.sui.bigdata.sml.web.misc.k8s.KubeComponentMaker;
import com.sui.bigdata.sml.web.misc.k8s.ServingInfo;
import com.sui.bigdata.sml.web.misc.k8s.ServingModel;
import com.sui.bigdata.sml.web.repository.mapper.ModelMapper;
import com.sui.bigdata.sml.web.repository.model.Model;
import com.sui.bigdata.sml.web.repository.model.ModelGroup;
import com.sui.bigdata.sml.web.service.ModelGroupService;
import com.sui.bigdata.sml.web.service.impl.ModelGroupServiceImpl;
import com.sui.bigdata.sml.web.util.BaseHolder;
import io.kubernetes.client.ApiException;
import io.kubernetes.client.models.V1Deployment;
import ma.glasnost.orika.MapperFacade;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.List;

/**
 * @author chq
 * @date 2020-12-23 15:45:08
 * @description 接入Disconf配置并监听指定key（副本个数、tf模型配置参数（如有））重新部署pod
 */
@Configuration
public class DisconfAutoConfig implements ConfigUpdaterKey {
    private static Logger logger = LoggerFactory.getLogger(DisconfAutoConfig.class);
    private ModelGroupService modelGroupService;
    private MapperFacade mapperFacade;
    private ModelMapper modelMapper;
    private ServingInfo servingInfo;
    private ModelGroupServiceImpl modelGroupServiceImpl;

    /*
     * @Author: chq
     * @desc: 接入disconf配置，并开启监听模式
     * @date: 2020/12/23 15:48
     */
    @Bean(name = "disconfConfig", initMethod = "init", destroyMethod = "destroy")
    public ReloadablePropertiesFactoryBean reloadablePropertiesFactoryBean() {
        ReloadablePropertiesFactoryBean bean = new ReloadablePropertiesFactoryBean();
        bean.setEnableDisconf(true);
        //注意，开启key的监听必须要set次方法
        bean.setKeyListener(DisconfAutoConfig.class.getName());
        // 读取 disconf 该目录下的所有配置
        bean.setPath("sml-web/1_0_0_0");
        return bean;
    }

    /*
     * @Author: chq
     * @desc: 若监听到key发生改变，执行此方法
     * @date: 2020/12/23 15:48
     */
    @Override
    public void reloadDisconfKey(String key, String oldValue, String newValue) {
        {
            try {
                //判断待监听的key后缀
                if (key.endsWith("jvm.args") || key.endsWith("deployment.replicas") || key.endsWith("tensorflow.args")) {
                    logger.info("请注意:{}值被改变，原值为:{},新值为{},即将加载新值重新部署pod", key, oldValue, newValue);

                    //注入指定对象到SpringIOC中，此处不用@Autowire的原因是无法注入，需要手动写工具类BaseHolder获取上下文注入
                    initObject();
                    logger.info("Spring手动注入下列对象至IOC：modelGroupService={},modelGroupServiceImpl={},mapperFacade={}," +
                            "modelMapper={}", modelGroupService, modelGroupServiceImpl, mapperFacade, modelMapper);

                    // 获取serverToken
                    String serverToken = key.split("\\.")[0];
                    logger.info("待重新部署的serverToken={}", serverToken);

                    //获取模型组中子模型的的详细信息
                    ModelGroupDTO modelGroupDTO = modelGroupService.getByServingToken(serverToken);
                    ModelGroup group = mapperFacade.map(modelGroupDTO, ModelGroup.class);
                    String servingToken = group.getServingToken();
                    String modelTokens = group.getModelTokens();
                    List<Model> models = modelGroupServiceImpl.listModelsByModelStrWithDelimiter(modelTokens, ",");
                    logger.info("子模型token={},子模型详细信息models={}", modelTokens, JSON.toJSONString(models));

                    //获取servingInfo
                    servingInfo = new ServingInfo(servingToken, mapperFacade.mapAsList(models, ServingModel.class));
                    logger.info("servingInfo={}", JSON.toJSONString(servingInfo));

                    //根据disconf中修改的副本个数或tf模型的配置动态重新部署pod
                    reDeploy(servingToken, new KubeComponentMaker(servingInfo).getDeployment());
                }
            } catch (Exception e) {
                logger.error("重新部署pod出错,{},{}", e, e.getMessage());
            }
        }
    }

    private void initObject() {
        modelGroupService = BaseHolder.getBean(ModelGroupService.class);
        mapperFacade = BaseHolder.getBean(MapperFacade.class);
        modelMapper = BaseHolder.getBean(ModelMapper.class);
        modelGroupServiceImpl = BaseHolder.getBean(ModelGroupServiceImpl.class);
    }

    public void reDeploy(String servingToken, V1Deployment deployment) throws ApiException {
        logger.info("正在重新部署pod-------------》");
        V1Deployment result = KubeConfig.deploymentAPI.replaceNamespacedDeployment(KubeConfig.NAME_PREFIX + servingToken,
                KubeConfig.NAMESPACE, deployment, null, null, null);
        logger.info("重新部署后对应的serverToken:{},可用pod个数为:{}", servingToken, result.getStatus().getAvailableReplicas());
        logger.info("等待pod重新部署完成...........请移步至K8s集群查看pod存活age以验证");
    }
}
